﻿using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System;
using CdgNetPersistencia.ClasesBases;
using CdgNetPersistencia.Componentes;
using System.Reflection;

/**
    * Autor :  Cristian Daniel Garay Sosa
    * Fecha :  26/03/2012 (v2)
    * Comentarios:
    *          gestiona la interaccion de la aplicacion con los datos de una tabla y sus relaciones
    * 
    * Cambios:
    *          26/03/2012 - CDGS - Agregado Set_registros()
    *                              Transfiere los datos de un registro de tabla a su OTD correspondiente
    * 
    * **/

namespace CdgNetPersistencia.ClasesBases
{
    /// <summary>
    /// Clase base de estructura de un administrador de Tabla
    /// </summary>
    public abstract class ADMbase
    {

        #region PROPIEDADES DE LA CLASE

        public string NOMBRE_CLASE = "ADMbase";

        /// <summary>
        /// Instancia relacionada de la clase TBLbase
        /// </summary>
        protected TBLbase _oTabla;

        /// <summary>
        /// Instancia de la clase de utileria de conexion a la base de datos
        /// </summary>
        protected ConectorBase _oConector;

        /// <summary>
        /// Coleccion de instancias de registros
        /// </summary>
        protected List<OTDbase> _lInstancias;


        #endregion



        #region CONSTRUCTORES

        /// <summary>
        /// Contructor por defecto
        /// </summary>
        public ADMbase()
        {}

        /// <summary>
        /// Contructor de la clase
        /// </summary>
        /// <param name="oConectorBase">Instancia de utileria de interaccion con la base de datos</param>
        public ADMbase(ConectorBase oConectorBase)
        {
            _oConector = oConectorBase;

        }


        #endregion



        #region GETTERS Y SETTERS

        /// <summary>
        /// Instancia relacionada de la clase TBLbase
        /// </summary>
        protected void Set_tabla(TBLbase oTablaParam)
        {
            //asignamos la instancia de estructura de tabla a manejar
            _oTabla = oTablaParam;

            //establecemos los comandos DML
            _Select_sql = _oTabla.Get_select_basico;
            _Insert_sql = _oTabla.Get_insert_parametros;
            _Update_sql = _oTabla.Get_update_parametros;
            _Delete_sql = _oTabla.Get_delete_basico;

        }
        

        /// <summary>
        /// Devuelve o establece el comando de seleccion de datos de la tabla
        /// </summary>
        protected string _Select_sql { get; set; }

        /// <summary>
        /// Devuelve o establece el comando de insercion de datos a la tabla
        /// </summary>
        protected string _Insert_sql { get; set; }

        /// <summary>
        /// Devuelve o establece el comando de actualizacion de datos de la tabla
        /// </summary>
        protected string _Update_sql { get; set; }

        /// <summary>
        /// Devuelve o establece el comando de aliminacion de datos de la tabla
        /// </summary>
        protected string _Delete_sql { get; set; }



        #endregion



        #region METODOS DML

        /// <summary>
        /// Inserta un nuevo registro en la tabla a partir de la instancia
        /// </summary>
        /// <param name="oOTDbase">Instancia de la clase que representa a un registro</param>
        /// <returns>Lista [entero, resultado]</returns>
        public abstract List<object> lAgregar(OTDbase oOTDbase);

        /// <summary>
        /// Actualiza el registro en la tabla a partir de la instancia
        /// </summary>
        /// <param name="oOTDbase">Instancia de la clase que representa a un registro</param>
        /// <returns>Lista [entero, resultado]</returns>
        public abstract List<object> lActualizar(OTDbase oOTDbase);

        /// <summary>
        /// Elimina el registro de la tabla representado por la instancia
        /// </summary>
        /// <param name="oOTDbase">Instancia de la clase que representa a un registro</param>
        /// <returns>Lista [entero, resultado]</returns>
        public abstract List<object> lEliminar(OTDbase oOTDbase);

        /// <summary>
        /// Obtiene una unica instancia que coincide con el Id buscado
        /// </summary>
        /// <param name="oOTDbase">Instancia de la clase que representa a un registro</param>
        /// <returns>Lista [entero, resultado]</returns>
        public abstract List<object> lGet_elemento(OTDbase oOTDbase);

        /// <summary>
        /// Obtiene una lista de instancias que coinciden con el criterio de filtrado
        /// </summary>
        /// <param name="cFiltroWhere">Criterio de filtrado de registros</param>
        /// <returns>Lista [entero, resultado]</returns>
        public abstract List<object> lGet_elementos(string cFiltroWhere);


        #endregion
        


        /// <summary>
        /// Ejecuta la instanciacion y asignacion de los OTDs 
        /// </summary>
        /// <typeparam name="T">Clase de OTDbase extendida</typeparam>
        /// <param name="lRegistros">Lista de OTDs a cargar</param>
        /// <param name="aFilasParam">Arreglo de filas de un DataTable</param>
        /// <see cref="http://www.csharp-examples.net/reflection-examples/"/>
        public List<T> Set_registros<T>(List<T> lRegistros, DataRowCollection aFilasParam)
        {
            //reinstanciamos
            lRegistros = new List<T>();
            
            //tomamos el tipo de la instancia
            Type tTipoOtTD = typeof(T);

            //tomamos los metodos de la clase
            MethodInfo[] aMetodos = tTipoOtTD.GetMethods();

            //recorremos las filas
            foreach (DataRow dr in aFilasParam)
            {
                //creamos una instancia del OTD extendido
                object oInstanciaOTD = Activator.CreateInstance<T>();

                //recorremos los metodos de la clase actual
                foreach(MethodInfo metodo in aMetodos)
                {
                    //http://msdn.microsoft.com/es-es/library/z919e8tw%28v=vs.80%29.aspx
                    //tomamos los atributos del tipo Campo que la clase contiene
                    object[] atributos = metodo.GetCustomAttributes(true);

                    //recorremos sus atributos
                    foreach(object atributo in atributos) 
                    {
                        if(atributo is CdgNetPersistencia.Atributos.Campo)
                        {
                            //casteamos explicitamente a su tipo
                            CdgNetPersistencia.Atributos.Campo oCampo = (CdgNetPersistencia.Atributos.Campo)atributo;

                            //si el campo esta en la estrcutura actual
                            if (_oTabla.CAMPOS.ContainsKey(oCampo.GetNombre))
                            {
                                //tomamos la instancia correspondiente
                                CampoTabla oCampoTabla = _oTabla.CAMPOS[oCampo.GetNombre];

                                //llamamos al metodo setter la que hace referencia el campo actual
                                tTipoOtTD.InvokeMember(metodo.Name
                                                        , BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public
                                                        , null, oInstanciaOTD, new object[] { dr[oCampoTabla.nIndice] }
                                                        );
                            }
                            else
                            {
                                //sino, error
                                Exception ex = new Exception(string.Format("No se encuentra la referencia al campo {0} en la estructura de la tabla {1}"
                                                                 , oCampo.GetNombre
                                                                 , _oTabla.GetNombreTabla
                                                                 )
                                                        );
                                throw ex;

                            }
                        }
                    }
                }

                //agregamos la instancia nueva a la coleccion
                lRegistros.Add((T)oInstanciaOTD);

            }

            //devolvemos el resultado del metodo
            return lRegistros;

        }

        /// <summary>
        /// Devuelve la representacion de la clase
        /// </summary>
        /// <returns>Representacion de cadena de la clase</returns>
        public override string ToString()
        {
            return string.Format("<{0}: {1}> ", NOMBRE_CLASE, _oTabla.GetNombreTabla);
        }

    }
}
